﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace AhpilyServer
{
    /// <summary>
    /// 关于编码的工具类
    /// </summary>
    public static class EncodeTool
    {
        /// <summary>
        /// 反序列化对象
        /// </summary>
        public static DecodeObjDelegate mDecodeObjDelegate;

        public static EncodingObjDelegate mEncodingObjDelegate;


        #region 粘包拆包问题 封装一个有规定的数据报

        /// <summary>
        /// 构造消息体：消息头 + 消息尾
        /// </summary>
        /// <returns></returns>
        public static byte[] EncodePacket(byte[] data)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (BinaryWriter bw = new BinaryWriter(ms))
                {
                    // 先写入长度
                    bw.Write(data.Length);
                    // 在写入数据
                    bw.Write(data);

                    byte[] byteArray = new byte[(int) ms.Length];

                    Buffer.BlockCopy(ms.GetBuffer(), 0, byteArray, 0, byteArray.Length);

                    return byteArray;
                }
            }
        }

        /// <summary>
        /// 解析消息体 从缓存里取出一个个完整的数据包
        /// </summary>
        /// <returns></returns>
        public static byte[] DecodePacket(ref List<byte> dataCache)
        {
            // 四个字节 构成一个int32 的
            if (dataCache.Count < 4)
            {
                return null;
                //throw new Exception("数据缓存长度不足4 不能构成一个完整的消息");
            }

            using (MemoryStream memoryStream = new MemoryStream(dataCache.ToArray()))
            {
                using (BinaryReader binaryReader = new BinaryReader(memoryStream))
                {
                    // 包头
                    int length = binaryReader.ReadInt32();
                    int dataRemainLength = (int) (memoryStream.Length - memoryStream.Position);
                    if (length > dataRemainLength)
                    {
                        return null;
                        //throw new Exception("数据长度不够包头约定的长度");
                    }

                    byte[] data = binaryReader.ReadBytes(length);
                    // 更新一下数据缓存
                    dataCache.Clear();
                    dataCache.AddRange(binaryReader.ReadBytes(dataRemainLength));

                    return data;
                }
            }
        }

        #endregion

        #region 构造发送的SocketMsg

        /// <summary>
        /// 把socketMsg类转化成字节数组 发送出去
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static byte[] EncodeMsg(SocketMsg msg)
        {
            MemoryStream ms = new MemoryStream();

            BinaryWriter binaryWriter = new BinaryWriter(ms);


            binaryWriter.Write(msg.OpCode);
            binaryWriter.Write(msg.SubCode);

            // 如果不等于null 才需要把object 转化成字节数据 存起来
            if (msg.Value != null)
            {
                byte[] valueBytes;

                if (mEncodingObjDelegate != null)
                {
                    valueBytes = mEncodingObjDelegate(msg.Value);
                }
                else
                {
                    valueBytes = EncodeObj(msg.Value);
                }

                binaryWriter.Write(valueBytes);
            }

            byte[] data = new byte[ms.Length];

            Buffer.BlockCopy(ms.GetBuffer(), 0, data, 0, (int) ms.Length);

            binaryWriter.Close();
            ms.Close();

            return data;
        }

        /// <summary>
        /// 将收到的字节数组 转化成 SocketMsg对象
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static SocketMsg DecodeMsg(byte[] data)
        {
            MemoryStream ms = new MemoryStream(data);
            BinaryReader binaryReader = new BinaryReader(ms);

            SocketMsg msg = new SocketMsg();

            msg.OpCode = binaryReader.ReadInt32();
            msg.SubCode = binaryReader.ReadInt32();

            // 还有剩余的字节没有读 代表 value 有值
            if (ms.Length > ms.Position)
            {
                byte[] valueBytes = binaryReader.ReadBytes((int) (ms.Length - ms.Position));

                object value;
                if (mDecodeObjDelegate == null)
                {
                    // 没有就用默认的自己写的
                    value = DecodeObj(valueBytes);
                }
                else
                {
                    // 有设置自己的序列化工具就用用户自己的
                    value = mDecodeObjDelegate(valueBytes);
                }


                msg.Value = value;
            }

            binaryReader.Close();
            ms.Close();
            return msg;
        }

        #endregion


        #region 把一个object 类型转换成byte[]

        /// <summary>
        /// 序列化对象
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static byte[] EncodeObj(object value)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter();

                bf.Serialize(ms, value);

                byte[] valueBytes = new byte[ms.Length];
                Buffer.BlockCopy(ms.GetBuffer(), 0, valueBytes, 0, (int) ms.Length);

                return valueBytes;
            }
        }


        /// <summary>
        /// 反序列化对象
        /// </summary>
        /// <param name="valueBytes"></param>
        /// <returns></returns>
        private static object DecodeObj(byte[] valueBytes)
        {
            using (MemoryStream ms = new MemoryStream(valueBytes))
            {
                BinaryFormatter bf = new BinaryFormatter();
                object value = bf.Deserialize(ms);
                return value;
            }
        }

        public delegate object DecodeObjDelegate(byte[] valueBytes);



        public delegate byte[] EncodingObjDelegate(object Obj);

        #endregion
    }
}